#ifndef Scenic_Renderer_common_h
#define Scenic_Renderer_common_h

#include <stdio.h>
#include <math.h>

#ifndef M_PI
	#define M_PI 3.1415926535
#endif

#ifdef OPT_X11
	#define __stdcall
#endif

#ifdef DIRECTX
#define strdup _strdup
#endif

#include "Resource.h"
#include "Graphics.h"
#include <GL/glu.h>


#ifndef max
	#define max(a, b) ((a) > (b) ? (a) : (b))
	#define min(a, b) ((a) < (b) ? (a) : (b))
#endif


namespace Scenic
{
namespace Renderer
{
struct Context;
struct Affine2;
struct ClipArea;
class Image;
}
}

#include "VertexStore.h"

namespace Scenic
{
namespace Renderer
{

enum CapStyle
{
	BUTT_CAP = 0,
	ROUND_CAP = 1,
	SQUARE_CAP = 2
};

enum JoinStyle
{
	MITER_JOIN = 0,
	ROUND_JOIN = 1,
	BEVEL_JOIN = 2
};

enum GradientType
{
	LINEAR = 0,
	RADIAL = 1,
	SQUARE = 2,
	CONICAL = 3
};

enum SurfaceType
{
	SURFACE_TYPE_COLOR = 0,
	SURFACE_TYPE_ALPHA = 1,
	SURFACE_TYPE_ALPHA2X = 2,
	SURFACE_TYPE_ALPHA4X = 3
};

enum AttributeType
{
	ATTRIBUTE_TYPE_CHAR = 0,
	ATTRIBUTE_TYPE_SHORT = 1,
	ATTRIBUTE_TYPE_INT = 2,
	ATTRIBUTE_TYPE_FLOAT = 3
};

enum Attribute
{
	POSITION_ATTRIBUTE = 0,
	COLOR_ATTRIBUTE = 1,
	TEX_COORD1_ATTRIBUTE = 2,
	ATTRIBUTE_SIZE
};

enum FragmentParameter
{	
	COLOR_FP = 0,
	FP_SIZE = 1
};

#define PRIMITIVE_MAX_TEXTURES 1

struct RGBAFloat
{
	float red;
	float green;
	float blue;
	float alpha;

	int getRGBA32() const;
};

struct Float2
{
	float x;
	float y;
};

struct Double2
{
	double x;
	double y;
};

struct Affine2
{
	double m00;
	double m01;
	double m10;
	double m11;
	double dx;
	double dy;

	Affine2 inverse();
	Double2 transform(const Double2 &p)
	{
		Double2 r;
		
		r.x = p.x * m00 + p.y * m01 + dx;
		r.y = p.x * m01 + p.y * m11 + dy;
		
		return r;
	}
};

#define TESS_BLOCK_SIZE 100
 
struct TessVertex
{
	double p[3];
};
 
struct TessBlock
{
	struct TessVertex vertices[TESS_BLOCK_SIZE];
	struct TessBlock *next;
};

class LineRenderer
{
	Context *context;
	
	bool isClosed;
	Double2 firstp;
	Double2 firstd;
	Double2 prevp;
	Double2 prevd;
	int count;
	int color;
	double width;
	double minu;
	double maxu;
	double miterLimit;
	bool closeStroke;
	VertexStore *vs;
	double dashLength;
	double dashPhase;

public:
	LineRenderer(Context *context, bool isClosed);

	void begin();
	void end();
	void addPoint(const Double2 &p);
private:
	void draw();
	void createEdgeTexture();
	void createDashTexture();
	void drawLine(const Double2 &p1, const Double2 &p2, const Double2 &d, double length);
	void drawCap(const Double2 &p, const Double2 &d);
	void drawSquareCap(const Double2 &p, const Double2 &d);
	void drawRoundCap(const Double2 &p, const Double2 &d);
	void drawJoin(const Double2 &p, const Double2 &d1, const Double2 &d2);
	void drawBevelJoin(const Double2 &p, const Double2 &d1, const Double2 &d2);
	void drawMiterJoin(const Double2 &p, const Double2 &d1, const Double2 &d2);
	void drawRoundJoin(const Double2 &p, const Double2 &d1, const Double2 &d2);
};

class PolygonRenderer
{
public:
	virtual ~PolygonRenderer() { }

	virtual void begin() = 0;
	virtual void end() = 0;
	virtual void beginContour() = 0;
	virtual void endContour() = 0;
	virtual void addPoint(double x, double y) = 0;
};

struct ClipArea
{
	ClipArea *parent;
	int x;
	int y;
	int width;
	int height;
	int offsetX;
	int offsetY;
	int type;
	Surface *surface;
	Texture *texture;
	int textureWidth;
	int textureHeight;

	int getModulation();
};

class TessVertexBuffer
{
	TessBlock *tessBlocks;
	int numTessVertex;

public:
	TessVertexBuffer();
	~TessVertexBuffer();

	TessVertex *newVertex();
	void free();
};

#define LINE_PRIMITIVES_TYPE 1

class Primitives
{
	Context *context;
	VertexStore *vertexStore;
public:
	Primitives(Context *context);
	virtual ~Primitives();

	Context *getContext() { return context; }
	void setContext(Context *context) { this->context = context; };
	void setVertexStore(VertexStore *vs) { vertexStore = vs; }
	VertexStore *getVertexStore() { return vertexStore; }

	virtual int getType() = 0;
	virtual void draw() = 0;
};

struct AttributeInfo
{
	int type;
	int size;
	char *data;
	int length;

	AttributeInfo()
	{
		size = 0;
		data = 0;
		length = 0;
	}
};

struct ScenePrimitives
{
	int type;
	AttributeInfo attributes[ATTRIBUTE_SIZE];
	float fragmentParameters[FP_SIZE][4];
	Image *textures[1];

	ScenePrimitives()
	{
		textures[0] = 0;
	}
};

struct Context : public Resource
{
#ifdef DIRECTX
	HWND hwnd;
#endif
#ifdef OPT_X11
	Window window;
#endif
	Device *device;

	bool polygonAntialias;
	Affine2 transform;
	RGBAFloat color;

	TessVertexBuffer tessVertexBuffer;

	VertexStore *glyphVs;

	PolygonRenderer *polygonRenderer;
	Texture *aaPolygonTexture;

	float lineWidth;
	int lineCap;
	int lineJoin;
	float miterLimit;
	DynArray<double> lineDashLengths;
	double lineDashPhase;
	Texture *lineDashTexture;
	LineRenderer *lineRenderer;
	Texture *lineEdgeTexture;

	ClipArea *clip;
	ClipArea *oldClips[1];

	Primitives *primitives;
	ScenePrimitives scenePrimitives;

	Context();
	~Context();

	bool init();
	void reset();
	void freeDeviceResources();
	void setPrimitives(Primitives *p);
	void setRenderTarget(Surface *target);
	void initializeViewport(int width, int height);
};

struct ImageBlock
{
	Texture *texture;
	int x;
	int y;
	int width;
	int height;
	float maxS;
	float maxT;
};

class Image : public Resource
{
	ImageBlock *blocks;
	int numCols;
	int numRows;
public:
	int width;
	int height;
	Format format;
	int options;

	Image();
	~Image();

	int getNumCols() { return numCols; }
	int getNumRows() { return numRows; }
	ImageBlock *getBlock(int row, int col);

	bool create(int width, int height, Format format, int options = 0);
	void free(bool isAsync = false);
	void write(int x, int y, int width, int height, char *data, int pitch);
	bool contentsLost();
private:
	void updateJavaObject();
};

Context *getContext(int id);

Image *getImage(int id);

char *getErrorName(int code);

}
}

#endif
